package com.xckj.cycling.utils

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import com.mapbox.geojson.*
import com.mapbox.maps.GeoJSONSourceData
import com.mapbox.maps.LayerPosition
import com.mapbox.maps.MapView
import com.mapbox.maps.extension.style.layers.Layer
import com.mapbox.maps.extension.style.layers.addPersistentLayer
import com.mapbox.maps.extension.style.layers.generated.LineLayer
import com.mapbox.maps.extension.style.layers.getLayer
import com.mapbox.maps.extension.style.layers.properties.generated.LineCap
import com.mapbox.maps.extension.style.sources.addSource
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
import com.mapbox.maps.extension.style.sources.getSource
import com.mapbox.maps.plugin.annotation.AnnotationConfig
import com.mapbox.maps.plugin.annotation.AnnotationManager
import com.mapbox.maps.plugin.annotation.annotations
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions
import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager
import com.mapbox.navigation.base.route.NavigationRoute
import com.mapbox.navigation.ui.maps.route.RouteLayerConstants
import com.xckj.cycling.R
import com.xckj.cycling.context.AppContext
import com.xckj.cycling.model.DistanceAndDuration
import com.xckj.cycling.model.RoutePoint
import kotlin.random.Random

class MapUtils(private val mapView: MapView) {
    private val pointAnnotationManager = mapView.annotations.createPointAnnotationManager(
        mapView,
        AnnotationConfig(layerId = "marker")
    )
    private var lineLayer: LineLayer? = null
    fun addAnnotationToMap(
        point: Point,
        resId: Int = R.mipmap.red_marker,
        clearBefore: Boolean = false
    ) {
// Create an instance of the Annotation API and get the PointAnnotationManager.
        bitmapFromDrawableRes(
            AppContext.globalContext(),
            resId
        )?.let {
            if (clearBefore) {
                pointAnnotationManager.deleteAll()
            }
// Set options for the resulting symbol layer.
            val pointAnnotationOptions: PointAnnotationOptions = PointAnnotationOptions()
// Define a geographic coordinate.
                .withPoint(point)
//                .withDraggable(true)
// Specify the bitmap you assigned to the point annotation
// The bitmap will be added to map style automatically.
                .withIconImage(it)
// Add the resulting pointAnnotation to the map.
            pointAnnotationManager.create(pointAnnotationOptions)
        }
    }

    fun addLineLayer() {
        this.lineLayer = LineLayer(LINE_ID, LINE_SOURCE).let {
            it.lineColor(Color.parseColor("#04dca6"))
            it.lineWidth(5.0)
            it.lineCap(LineCap.ROUND)
            mapView.getMapboxMap().getStyle()?.apply {
                addPersistentLayer(it)
                val s = getSource(LINE_SOURCE)
                if (s == null) {
                    addSource(
                        GeoJsonSource.Builder(LINE_SOURCE).featureCollection(
                            FeatureCollection.fromFeatures(
                                arrayOf()
                            )
                        ).build()
                    )
                } else {
                    val geoJsonSource: GeoJsonSource = s as GeoJsonSource
                    geoJsonSource.featureCollection(
                        FeatureCollection.fromFeatures(
                            arrayOf()
                        )
                    )
                }

            }
            it
        }
    }

    private val points: MutableList<Point> = mutableListOf()
    fun addPointToLine(point: Point) {
//        val random = Random.nextInt(5000)
//        val p = Point.fromLngLat(
//            point.longitude() - random / 100000f,
//            point.latitude() + random / 100000f
//        )
//        points.add(p)
        points.add(point)

        val lineString: LineString = LineString.fromLngLats(points)
        mapView.getMapboxMap().getStyle()?.apply {
            (getSource(LINE_SOURCE) as? GeoJsonSource)?.featureCollection(
                FeatureCollection.fromFeatures(
                    arrayOf(Feature.fromGeometry(lineString))
                )
            )

        }
    }
    fun getLineString(): LineString {
        return LineString.fromLngLats(points)
    }

    private fun removeLine() {
        points.clear()
        mapView.getMapboxMap().getStyle()?.apply {
            removeStyleLayer(LINE_ID)
        }
        this.lineLayer = null
    }


    fun clearAnnotation() {
        removeLine()
        pointAnnotationManager.deleteAll()
    }

    private fun bitmapFromDrawableRes(context: Context, @DrawableRes resourceId: Int) =
        convertDrawableToBitmap(AppCompatResources.getDrawable(context, resourceId))

    private fun convertDrawableToBitmap(sourceDrawable: Drawable?): Bitmap? {
        if (sourceDrawable == null) {
            return null
        }
        return if (sourceDrawable is BitmapDrawable) {
            sourceDrawable.bitmap
        } else {
// copying drawable object to not manipulate on the same reference
            val constantState = sourceDrawable.constantState ?: return null
            val drawable = constantState.newDrawable().mutate()
            val bitmap: Bitmap = Bitmap.createBitmap(
                drawable.intrinsicWidth, drawable.intrinsicHeight,
                Bitmap.Config.ARGB_8888
            )
            val canvas = Canvas(bitmap)
            drawable.setBounds(0, 0, canvas.width, canvas.height)
            drawable.draw(canvas)
            bitmap
        }
    }

    /**
     * 绘制从起点到终点
     */
    fun drawPoints(routePionts: MutableList<RoutePoint>) {
        val style = mapView.getMapboxMap().getStyle()
        val line = style?.let {
            it.getLayer(LINE_ID_ROUTES) as? LineLayer
        } ?: LineLayer(LINE_ID_ROUTES, LINE_ID_ROUTES).let {
            it.lineColor(Color.parseColor("#04DCA6"))
            it.lineWidth(5.0)
            it.lineCap(LineCap.ROUND)
            style?.addPersistentLayer(it)
            it
        }
        val source = (style?.getSource(LINE_ID_ROUTES) as? GeoJsonSource) ?: GeoJsonSource.Builder(
            LINE_ID_ROUTES
        ).featureCollection(
            FeatureCollection.fromFeatures(
                arrayOf()
            )
        ).build().let {
            style?.addSource(it)
            it
        }
        val lineString: LineString = LineString.fromLngLats(routePionts.map {
            Point.fromLngLat(it.lng, it.lat)
        })
        source?.let {
            it.featureCollection(
                FeatureCollection.fromFeatures(
                    arrayOf(
                        Feature.fromGeometry(
                            lineString
                        )
                    )
                )
            )
        }

    }

    fun drawLineString(lineString: LineString) {
        val style = mapView.getMapboxMap().getStyle()
        val line = style?.let {
            it.getLayer(LINE_ID_ROUTES_NAVIGATION) as? LineLayer
        } ?: LineLayer(LINE_ID_ROUTES_NAVIGATION, LINE_ID_ROUTES_NAVIGATION).let {
            it.lineColor(Color.RED)
            it.lineWidth(5.0)
            it.lineCap(LineCap.ROUND)
            val point = style?.getLayer("marker")
            if (point != null) {
                style.addPersistentLayer(it, LayerPosition(null, "marker", null))
            } else {
                style?.addPersistentLayer(it)
            }
            it
        }
        val source = (style?.getSource(LINE_ID_ROUTES_NAVIGATION) as? GeoJsonSource)
            ?: GeoJsonSource.Builder(LINE_ID_ROUTES_NAVIGATION).featureCollection(
                FeatureCollection.fromFeatures(
                    arrayOf()
                )
            ).build().let {
                style?.addSource(it)
                it
            }
        source.let {
            it.featureCollection(
                FeatureCollection.fromFeatures(
                    arrayOf(
                        Feature.fromGeometry(
                            lineString
                        )
                    )
                )
            )
        }
    }

    companion object {
        private const val LINE_ID_ROUTES = "line_routes"

        private const val LINE_ID_ROUTES_NAVIGATION = "line_routes_navigation"
        private const val LINE_ID = "navigation_line"
        private const val LINE_SOURCE = "line-source"

        @JvmStatic
        fun getTotalDistance(list: List<NavigationRoute>): DistanceAndDuration {
            var total = 0.0
            var duration = 0.0
            list.forEach {
                total += it.directionsRoute.distance()
                duration += it.directionsRoute.duration()
            }
            return DistanceAndDuration(total, duration)
        }
    }

}